package com.xiaoshuidi.cloud.module.contract.service.contract.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.SettlementOrderFeeVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.rentOrder.RentOrderCountVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.company.BatchFeeVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.company.BatchSettlementOrderFeeVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.company.CompanyContractRefundReqVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.settlement.*;
import com.xiaoshuidi.cloud.module.contract.convert.contract.ContractSettlementConvert;
import com.xiaoshuidi.cloud.module.contract.convert.contract.SettlementConvert;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.OrderFeesDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.RentOrderDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.*;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.OrderFeesMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.RentOrderMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.CompanyContractMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractRoomMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementDetailMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementMapper;
import com.xiaoshuidi.cloud.module.contract.enums.ApiConstants;
import com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderFeeFlowTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderFeeSortTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderPayStatusEnum;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractNodeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.settlement.SettlementStatusEnum;
import com.xiaoshuidi.cloud.module.contract.service.bill.OrderFeesService;
import com.xiaoshuidi.cloud.module.contract.service.bill.RentOrderService;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractRoomService;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractSettlementService;
import com.xiaoshuidi.cloud.module.contract.util.bill.BillPeriod;
import com.xiaoshuidi.cloud.module.finance.api.finance.FinanceApi;
import com.xiaoshuidi.cloud.module.finance.api.finance.dto.BankTransferReqDTO;
import com.xiaoshuidi.cloud.module.finance.api.order.PayOrderApi;
import com.xiaoshuidi.cloud.module.finance.api.order.dto.PayOrderCreateReqDTO;
import com.xiaoshuidi.cloud.module.finance.enums.finance.SourceBizTypeEnum;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.RoomHouseApi;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomHouseRespVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants.*;

/**
 * 合同结算
 *
 * @author lee
 */
@Service
@Slf4j
public class ContractSettlementServiceImpl implements ContractSettlementService {

    @Resource
    private OrderFeesService orderFeesService;
    @Resource
    private RentOrderService rentOrderService;
    @Lazy
    @Resource
    private ContractRoomService contractRoomService;
    @Resource
    private SettlementMapper settlementMapper;
    @Resource
    private SettlementDetailMapper settlementDetailMapper;
    @Resource
    private RoomHouseApi roomHouseApi;
    @Resource
    private FinanceApi financeApi;
    @Resource
    private ContractRoomMapper contractRoomMapper;
    @Resource
    private RentOrderMapper rentOrderMapper;
    @Resource
    private OrderFeesMapper orderFeesMapper;
    @Resource
    private CompanyContractMapper companyContractMapper;

    @Resource
    private PayOrderApi payOrderApi;

    /**
     * 合同截至到某一日期需要结算的费用
     *
     * @param contractId
     * @param endDate
     */
    public List<SettlementOrderFeeVO> settlement(Long contractId, LocalDate endDate, String settlementType) {
        rentOrderService.unpaidOrder(endDate, Collections.singletonList(contractId),
                Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
        ContractRoomDO contract = contractRoomService.get(contractId);
        if (contract == null) {
            throw new ServiceException(ErrorCodeConstants.CONTRACT_NOT_EXISTS);
        }
        // 合同所有账单
        List<RentOrderDO> rentOrders = rentOrderService.getRentOrderByContractId(contractId);
        // 合同所有费用
        List<OrderFeesDO> fees = orderFeesService.getOrderFeeListByContractId(contractId);
        // 续租合同
        ContractRoomDO renewContract = contractRoomService.getByFromId(contract.getId());
        if (renewContract != null) {
            List<RentOrderDO> renewRentOrder = rentOrderService.getRentOrderByContractId(renewContract.getId());
            boolean hasCarryForward = false;
            for (RentOrderDO rentOrderDO : renewRentOrder) {
                if (OrderPayStatusEnum.PAID.getCode().equals(rentOrderDO.getPayStatus())
                        || OrderPayStatusEnum.PART.getCode().equals(rentOrderDO.getPayStatus())) {
                    rentOrders.add(rentOrderDO);
                    hasCarryForward = true;
                }
            }
            if (hasCarryForward) {
                List<OrderFeesDO> deposit = fees.stream().filter(f -> OrderFeeSortTypeEnum.DEPOSIT.getCode().equals(f.getFeeSort()) && 2 == f.getFeeId()).collect(Collectors.toList());
                // 续租合同已经把原合同的押金结转了, 这里移除原合同的押金
                fees.removeAll(deposit);
            }
            List<OrderFeesDO> l = orderFeesService.getOrderFeeListByContractId(renewContract.getId());
            fees.addAll(l.stream().filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                    .peek(f -> f.setAmount(f.getPaidAmount()))
                    .collect(Collectors.toList()));

        }
        // 已支付需要结算的账单
        List<RentOrderDO> settlementOrders = rentOrders.stream()
                // 自动生成和手动添加的账单
                .filter(o -> OrderTypeEnum.DEFAULT.getCode().equals(o.getOrderType())
                        || OrderTypeEnum.CUSTOM.getCode().equals(o.getOrderType()))
                // 当期及以后的账单
                .filter(o -> o.getStartTime().compareTo(endDate) <= 0 && o.getEndTime().compareTo(endDate) >= 0
                        || o.getStartTime().isAfter(endDate))
                // 已经支付或部分支付
                .filter(o -> OrderPayStatusEnum.PAID.getCode().equals(o.getPayStatus())
                        || OrderPayStatusEnum.PART.getCode().equals(o.getPayStatus()))
                .collect(Collectors.toList());
        Map<Long, RentOrderDO> rentOrderIds = settlementOrders.stream().collect(Collectors.toMap(RentOrderDO::getId, Function.identity()));

        // 押金费用
        List<OrderFeesDO> deposit = fees.stream()
                .filter(f -> OrderFeeSortTypeEnum.DEPOSIT.getCode().equals(f.getFeeSort()))
                .filter(f -> 2 == f.getFeeId())
                .filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                // 只要是提前退, 就不退押金
                .filter(f -> ContractNodeEnum.EXCHANGE.getCode().equals(settlementType) || contract.getEndTime().compareTo(endDate) <= 0)
                .collect(Collectors.toList());

        // 其它已支付周期费用(只结转周期性费用)
        List<OrderFeesDO> others = fees.stream()
                .filter(f -> OrderFeeSortTypeEnum.CYCLE.getCode().equals(f.getFeeSort()))
                .filter(f -> rentOrderIds.containsKey(f.getOrderId()))
                .sorted(Comparator.comparing(OrderFeesDO::getOrderId))
                .collect(Collectors.toList());
        // 历史合同的押金费用(除合同押金外的其他押金)
        List<OrderFeesDO> historyFees = getHistoryDepositFeesList(contract);
        deposit.addAll(historyFees);
        // 计算实际产生费用
        List<SettlementOrderFeeVO> list = calculateActualFeeByDailyFee(contract, others, endDate, rentOrderIds);
        // 押金及待结转费用
        list.addAll(deposit.stream().map(d -> convert(d, endDate)
                .setRefundableAmount(d.getAmount())).collect(Collectors.toList()));
        return list;
    }

    private List<OrderFeesDO> getHistoryDepositFeesList(ContractRoomDO contract){
        List<ContractRoomDO> subtree = new ArrayList<>();
        if (Objects.nonNull(contract.getFromId()) && 0L != contract.getFromId()){
            subtree = getSubtree(contract.getFromId());
        }
        subtree.add(contract);
        List<Long> contractIds = subtree.stream().map(ContractRoomDO::getId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(contractIds)){
            List<OrderFeesDO> orderFeeListByContractIds = orderFeesService.getOrderFeeListByContractIds(contractIds);
            if (CollectionUtils.isNotEmpty(orderFeeListByContractIds)){
                return orderFeeListByContractIds.stream().filter(f -> OrderFeeSortTypeEnum.DEPOSIT.getCode().equals(f.getFeeSort()) && 2 != f.getFeeId()).collect(Collectors.toList());
            }
        }
        return null;
    }

    public List<ContractRoomDO> getSubtree(Long fromId) {
        List<ContractRoomDO> subtree = new ArrayList<>();
        ContractRoomDO fromContractRoom = contractRoomService.get(fromId);
        subtree.add(fromContractRoom);
        if (Objects.nonNull(fromContractRoom) && Objects.nonNull(fromContractRoom.getFromId()) && 0L != fromContractRoom.getFromId()) {
            // 递归获取子类别
            subtree.addAll(getSubtree(fromContractRoom.getFromId()));
        }
        return subtree;
    }


    @Override
    public SettlementDO createSettlement(SettlementCreateReqVO createReqVO) {
        // 插入
        SettlementDO settlement = SettlementConvert.INSTANCE.convert(createReqVO);

        settlementMapper.insert(settlement);

        return settlement;
    }

    @Override
    public ContractSettlementListReqVO getSettlementByContractId(Long contractId) {
        LambdaQueryWrapper<SettlementDO> qw = new LambdaQueryWrapper<>();
        qw.eq(SettlementDO::getContractId, contractId)
                .notIn(SettlementDO::getClearedStatus, SettlementStatusEnum.REJECT.getCode())
                .orderByDesc(SettlementDO::getCreateTime);
        //查询总清单
        List<SettlementDO> settlementDOS = settlementMapper.selectList(qw);
        if (CollectionUtils.isEmpty(settlementDOS)) {
            return null;
        }
        SettlementDO settlementDO = settlementDOS.get(0);
        ContractSettlementListReqVO reqVO = ContractSettlementConvert.INSTANCE.convert(settlementDO);
        //查询合同周期
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(contractId);
        if (contractRoomDO != null) {
            reqVO.setStartTime(contractRoomDO.getStartTime());
            reqVO.setEndTime(contractRoomDO.getEndTime());
        }
        //查询清单明细
        List<SettlementDetailDO> settlementDetailDOS = settlementDetailMapper.selectList(SettlementDetailDO::getSettlementId, settlementDO.getId());

        return processingData(settlementDetailDOS, reqVO);
    }

    private ContractSettlementListReqVO processingData(List<SettlementDetailDO> settlementDetailDOS, ContractSettlementListReqVO reqVO) {
        List<SettlementDetailBaseVO> receivable = new ArrayList<>();
        List<SettlementDetailBaseVO> refundable = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(settlementDetailDOS)) {
            for (SettlementDetailDO settlementDetailDO : settlementDetailDOS) {
                SettlementDetailBaseVO settlementDetailBaseVO = ContractSettlementConvert.INSTANCE.convert(settlementDetailDO);
                if (settlementDetailDO.getFlowType().equals("in")) {
                    settlementDetailBaseVO.setFlowType("in");
                    settlementDetailBaseVO.setOrderStartDate(reqVO.getStartTime().atStartOfDay());
                    settlementDetailBaseVO.setOrderEndDate(reqVO.getEndTime().atStartOfDay());
                    settlementDetailBaseVO.setRefundableStartDate(reqVO.getEvictionTime().plusDays(1));
                    settlementDetailBaseVO.setRefundableEndDate(reqVO.getEvictionTime());
                    receivable.add(settlementDetailBaseVO);
                }
                //金额小于0，流出
                if (settlementDetailDO.getFlowType().equals("out")) {
                    settlementDetailBaseVO.setFlowType("out");
                    settlementDetailBaseVO.setRefundableStartDate(reqVO.getEvictionTime().plusDays(1));
                    settlementDetailBaseVO.setRefundableEndDate(settlementDetailBaseVO.getOrderEndDate().toLocalDate());
                    refundable.add(settlementDetailBaseVO);
                }
            }
            reqVO.setReceivable(receivable);
            reqVO.setRefundable(refundable);
        }
        return reqVO;
    }

    /**
     * 计算实际生产费用
     *
     * @param contract 合同
     * @param list     需要结算的已支付账单
     * @param endDate  提前结束日期
     */
    private List<SettlementOrderFeeVO> calculateActualFeeByDailyFee(ContractRoomDO contract, List<OrderFeesDO> list, LocalDate endDate, Map<Long, RentOrderDO> rentOrderDOMap) {
        return list.stream()
                .filter(fee -> fee.getAmount().compareTo(BigDecimal.ZERO) > 0)
                .map(fee -> {
                            // 已消费金额
                            BigDecimal totalConsume = BigDecimal.ZERO;
                            BigDecimal couponDiscount = rentOrderDOMap.get(fee.getOrderId()).getCouponDiscount();
                            couponDiscount = couponDiscount == null ? BigDecimal.ZERO : couponDiscount;
                            // 优惠券只优惠租金
                            if (couponDiscount.compareTo(BigDecimal.ZERO) > 0 && !ApiConstants.RENT_FEE_ID.equals(fee.getFeeId())) {
                                couponDiscount = BigDecimal.ZERO;
                            }
                            // 已经结束的账单全部扣除
                            if (fee.getEndTime().compareTo(endDate) <= 0) {
                                totalConsume = fee.getAmount().subtract(fee.getDiscountAmount()).subtract(couponDiscount);
                            } else if (fee.getStartTime().compareTo(endDate) > 0) {
                                // 未来的账单
                            } else {
                                BigDecimal monthlyAmount = fee.getAmount().subtract(fee.getDiscountAmount())
                                        .subtract(couponDiscount)
                                        .divide(BigDecimal.valueOf(contract.getPayMethodF()), 2, RoundingMode.HALF_UP);
                                totalConsume = totalConsume.add(BillPeriod.totalFee(fee.getStartTime(), endDate, monthlyAmount, fee.getEndTime().isEqual(endDate), fee.getEndTime().isEqual(endDate)));

                            }
                            // 待结转
                            return convert(fee, endDate)
                                    .setAmount(fee.getAmount().subtract(fee.getDiscountAmount()))
                                    .setRefundableAmount(fee.getAmount().subtract(fee.getDiscountAmount())
                                            .subtract(couponDiscount)
                                            .subtract(totalConsume.setScale(2, RoundingMode.HALF_UP)));
                        }
                ).collect(Collectors.toList());
    }

    private SettlementOrderFeeVO convert(OrderFeesDO fee, LocalDate endDate) {
        return new SettlementOrderFeeVO().setFeeId(fee.getFeeId()).setFeeName(fee.getFeeName())
                .setAmount(fee.getAmount())
                .setOrderStartDate(fee.getStartTime())
                .setOrderEndDate(fee.getEndTime())
                .setRefundableEndDate(fee.getStartTime())
                .setRefundableEndDate(endDate);
    }


    @Override
    public ContractSettlementWithDetailRespVO getSettlementWithDetailByContractId(Long contractId) {
        //查询总清单
        LambdaQueryWrapper<SettlementDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SettlementDO::getContractId, contractId);
        queryWrapper.eq(SettlementDO::getClearedStatus, SettlementStatusEnum.CLEAR.getCode());
        SettlementDO settlementDO = settlementMapper.selectOne(queryWrapper);
        if (settlementDO == null) {
            return null;
        }
        ContractSettlementWithDetailRespVO respVO = SettlementConvert.INSTANCE.convertToSettlementWithDetail(settlementDO);
        //查询清单明细
        List<SettlementDetailDO> settlementDetailDOS = settlementDetailMapper.selectList(SettlementDetailDO::getSettlementId, settlementDO.getId());
        respVO.setDetailList(settlementDetailDOS);
        return respVO;
    }

    @Override
    public PageResult<ContractSettlementGetListRespVO> getSettlementList(ContractSettlementGetListReqVO reqVO) {
        Page<SettlementExtDO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        settlementMapper.pageContractList(page, reqVO);
        List<SettlementExtDO> data = page.getRecords();
        if (CollectionUtils.isNotEmpty(data)) {
            List<Long> ids = data.stream().map(SettlementExtDO::getRoomId).collect(Collectors.toList());
            CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(ids);

            List<ContractSettlementGetListRespVO> list = SettlementConvert.INSTANCE.convert(data);
            if (CollectionUtils.isNotEmpty(roomHouseByIds.getData())) {
                Map<Long, String> collect = roomHouseByIds.getData().stream().filter(o -> StringUtils.isNotBlank(o.getFullRoomName()))
                        .distinct().collect(Collectors.toMap(RoomHouseRespVo::getId, RoomHouseRespVo::getFullRoomName));
                if (MapUtil.isNotEmpty(collect)) {
                    for (ContractSettlementGetListRespVO datum : list) {
                        datum.setFullRoomName(collect.get(datum.getRoomId()));
                    }
                }

            }
            AtomicInteger index = new AtomicInteger((reqVO.getPageNo() - 1) * reqVO.getPageSize() + 1);
            List<ContractSettlementGetListRespVO> collect1 = list.stream().filter(f -> f.getClearedStatus() != null)
                    .peek(m -> {
                        m.setClearedStatus(transitionStatus(m.getClearedStatus()));
                        m.setSerialNumber(index.getAndIncrement());
                    }).collect(Collectors.toList());
            return new PageResult<>(collect1, page.getTotal());
        }

        return new PageResult<>(Collections.emptyList(), 0L);
    }

    private String transitionStatus(String clearedStatus) {
        if (StringUtils.isNotBlank(clearedStatus)) {
            //无需审批 和已生效的 都是待清算
            if (SettlementStatusEnum.NO_AUDIT_REQUITED.getCode().equals(clearedStatus) ||
                    SettlementStatusEnum.HAVE_TAKEN_EFFECT.getCode().equals(clearedStatus)) {
                return "treatClear";
            }
        }
        return clearedStatus;
    }

    /**
     * @param reqVO
     * @return
     */
    @Override
    public PageResult<ContractSettlementGetListRespVO> getSettlementCompanyBatchList(ContractSettlementGetListReqVO reqVO) {
        Page<SettlementExtDO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        settlementMapper.pageContractBatchList(page, reqVO);
        List<SettlementExtDO> data = page.getRecords();
        if (CollectionUtils.isNotEmpty(data)) {
            List<ContractSettlementGetListRespVO> list = SettlementConvert.INSTANCE.convert(data);
            AtomicInteger index = new AtomicInteger((reqVO.getPageNo() - 1) * reqVO.getPageSize() + 1);
            List<ContractSettlementGetListRespVO> collect1 = list.stream().filter(f -> f.getClearedStatus() != null)
                    .peek(m -> {
                        m.setClearedStatus(transitionStatus(m.getClearedStatus()));
                        m.setSerialNumber(index.getAndIncrement());
                    }).collect(Collectors.toList());
            return new PageResult<>(collect1, page.getTotal());
        }

        return new PageResult<>(Collections.emptyList(), 0L);
    }

    @Override
    public PageResult<ContractSettlementCompanyListRespVO> getSettlementCompanyList(ContractSettlementGetListReqVO reqVO) {
        Page<ContractSettlementCompanyListRespVO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        settlementMapper.pageCompanyContractList(page, reqVO);
        PageResult<ContractSettlementCompanyListRespVO> result = new PageResult<>();
        result.setList(page.getRecords());
        result.setTotal(page.getTotal());
        return result;
    }

    @Override
    public ContractSettlementCompanyListReqVO getSettlementCompany(Long id) {
        SettlementDO settlementDO = settlementMapper.selectById(id);
        if (settlementDO != null) {
            ContractSettlementCompanyListReqVO respVO = SettlementConvert.INSTANCE.convert(settlementDO);
            List<SettlementDO> settlementDOS = settlementMapper.selectList(SettlementDO::getParentSettlementId, id);
            if (CollectionUtils.isNotEmpty(settlementDOS)) {
                for (SettlementDO aDo : settlementDOS) {
                    if (StringUtils.isNotBlank(aDo.getRoomName()))
                        respVO.getRoomNames().add(aDo.getRoomName());
                }
            }
            return respVO;
        }
        return null;
    }

    @Override
    public CompanyContractRefundReqVO getSettlementCompanyCompile(Long id) {
        return null;
    }

    @Override
    public ContractSettlementListReqVO getSettlementById(Long id) {
        //查询总清单
        SettlementDO settlementDO = settlementMapper.selectById(id);
        if (settlementDO == null) {
            return null;
        }
        ContractSettlementListReqVO reqVO = ContractSettlementConvert.INSTANCE.convert(settlementDO);
        //查询合同周期
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(settlementDO.getContractId());
        if (contractRoomDO != null) {
            reqVO.setStartTime(contractRoomDO.getStartTime());
            reqVO.setEndTime(contractRoomDO.getEndTime());
        }
        //查询清单明细
        List<SettlementDetailDO> settlementDetailDOS = settlementDetailMapper.selectList(SettlementDetailDO::getSettlementId, settlementDO.getId());
        return processingData(settlementDetailDOS, reqVO);
    }

    @Transactional
    @Override
    public void settlementConfirm(SettlementReqVO reqVO) {
        SettlementDO settlementDO = settlementMapper.selectById(reqVO.getId());
        if (settlementDO == null) {
            return;
        }
        SettlementDO update = SettlementDO.builder()
                .id(settlementDO.getId())
                .clearedStatus(SettlementStatusEnum.CLEAR.getCode())
                .build();
        if (StringUtils.isNotBlank(reqVO.getRemark())) {
            if (StringUtils.isNotBlank(settlementDO.getRemark())) {
                update.setRemark(settlementDO.getRemark() + ";" + reqVO.getRemark());
            } else {
                update.setRemark(reqVO.getRemark());
            }
        }
        settlementMapper.updateById(update);
        ContractRoomDO contractRoomDO = contractRoomService.get(settlementDO.getContractId());
        settlementConfirm("租客", contractRoomDO, settlementDO, reqVO);
    }

    @Transactional
    @Override
    public void settlementCompanyBatchConfirm(SettlementReqVO reqVO) {
        SettlementDO settlementDO = settlementMapper.selectById(reqVO.getId());
        if (settlementDO == null) {
            return;
        }
        SettlementDO update = SettlementDO.builder()
                .id(settlementDO.getId())
                .clearedStatus(SettlementStatusEnum.CLEAR.getCode())
                .build();
        if (StringUtils.isNotBlank(reqVO.getRemark())) {
            if (StringUtils.isNotBlank(settlementDO.getRemark())) {
                update.setRemark(settlementDO.getRemark() + ";" + reqVO.getRemark());
            } else {
                update.setRemark(reqVO.getRemark());
            }
        }
        settlementMapper.updateById(update);
        CompanyContractDO companyContractDO =
                companyContractMapper.selectCompanyContractById(settlementDO.getContractId());

        BankTransferReqDTO bankTransferReqDTO = new BankTransferReqDTO();
        bankTransferReqDTO
                .setOperator(SecurityFrameworkUtils.getLoginUser().getNickname());
        bankTransferReqDTO.setBizDate(LocalDate.now());
        bankTransferReqDTO.setBizNo(settlementDO.getSettlementNo());
        bankTransferReqDTO.setBizType(SourceBizTypeEnum.SETTLEMENT_BILL.name());
        bankTransferReqDTO.setApartmentId(settlementDO.getApartmentId());
        bankTransferReqDTO.setApartmentName(settlementDO.getApartmentName());
        bankTransferReqDTO.setRoomId(settlementDO.getRoomId());
        bankTransferReqDTO.setRoomName(settlementDO.getRoomName());
        bankTransferReqDTO.setDepartment(settlementDO.getApartmentName());
        Optional.ofNullable(companyContractDO)
                .ifPresent(p -> {
                    bankTransferReqDTO.setContractNo(companyContractDO.getContractNo());

                });
        bankTransferReqDTO.setPayeeName(reqVO.getPayeeName());
        bankTransferReqDTO.setPayeeMobile(settlementDO.getCustomerPhone());
        bankTransferReqDTO.setPayeeType("企业");
        bankTransferReqDTO.setPayAmount(settlementDO.getAmount());
        bankTransferReqDTO.setPayeeBank(reqVO.getPayeeBank());
        bankTransferReqDTO.setPayeeBankBranch(reqVO.getPayeeBankBranch());
        bankTransferReqDTO.setPayeeBankCode(reqVO.getPayeeBankCode());
        bankTransferReqDTO.setPayeeBankCity(reqVO.getPayeeBankCity());
        bankTransferReqDTO.setPayeeBankProvince(reqVO.getPayeeBankProvince());
        bankTransferReqDTO.setPayeeBankAccount(reqVO.getPayeeBankAccount());
        bankTransferReqDTO.setReason(settlementDO.getEvictionReason());
        bankTransferReqDTO.setAttachment(reqVO.getAttachment());
        bankTransferReqDTO.setTenantId(settlementDO.getTenantId());
        bankTransferReqDTO.setDeptId(settlementDO.getDeptId());
        financeApi.bankTransfer(bankTransferReqDTO);


    }

    /**
     * 退租流入清算
     *
     * @param reqVO
     * @return
     */
    @Override
    public Long inflowLiquidation(PayOrderCreateReqDTO reqVO) {
        SettlementDO settlementDO = settlementMapper.selectById(reqVO.getSourceBizId());
        if (ObjectUtil.isEmpty(settlementDO)) {
            throw exception(SETTLEMENT_SETTLED_NOT_EXIST);
        }
        if (settlementDO.getFlowType().equals("out")) {
            throw exception0(SETTLEMENT_EXIST_UNCLEARABLE.getCode(), "仅能清算资金流入清算单");
        }
        //已经清算无法再次清算
        if (SettlementStatusEnum.CLEAR.getCode().equals(settlementDO.getClearedStatus())) {
            throw exception0(SETTLEMENT_EXIST_UNCLEARABLE.getCode(), "已清算无法重复清算");
        }
        //非生效以及无需审核的无法清算
        if (!CollectionUtil.newArrayList(SettlementStatusEnum.HAVE_TAKEN_EFFECT.getCode(),
                        SettlementStatusEnum.NO_AUDIT_REQUITED.getCode())
                .contains(settlementDO.getClearedStatus())) {
            throw exception0(SETTLEMENT_EXIST_UNCLEARABLE.getCode(), "该审核状态下无法清算");
        }
        if (settlementDO.getAmount().compareTo(BigDecimal.ZERO) == 0) {
            //0元直接清算
            return (long) settlementMapper.updateById(
                    SettlementDO.builder().id(settlementDO.getId()).clearedStatus(SettlementStatusEnum.CLEAR.getCode()).build()
            );
        }
        //调用finance 进行创建订单
        return payOrderApi.createOrder(reqVO).getData();
    }

    /**
     * 企业批量清算
     *
     * @param ids
     */
    @Override
    public void settlementCompanyBatchConfirmList(List<Long> ids) {
        LambdaQueryWrapper<SettlementDO> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.in(SettlementDO::getId, ids);
        List<SettlementDO> settlementDO = settlementMapper.selectList(lambdaQueryWrapper);
        if (CollectionUtils.isEmpty(settlementDO)) {
            return;
        }
        //是否存在 除 无需审批 和 已生效的 的结算端
        List<SettlementDO> collect = settlementDO.stream()
                .filter(item -> item.getClearedStatus().equals(SettlementStatusEnum.IN_REVIEW.getCode())
                        || item.getClearedStatus().equals(SettlementStatusEnum.REJECT.getCode())
                        || item.getClearedStatus().equals(SettlementStatusEnum.CLEAR.getCode())
                ).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(collect)) {
            throw exception(SETTLEMENT_SETTLED);
        }
        List<SettlementDO> flowList = settlementDO.stream().filter(item -> !item.getFlowType().equals("out"))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(flowList)) {
            throw exception(SETTLEMENT_EXIST_UNCLEARABLE);
        }
        settlementDO.forEach(item -> {
            item.setClearedStatus(SettlementStatusEnum.CLEAR.getCode());
        });
        //批量 修改结算单
        settlementMapper.updateBatch(settlementDO, settlementDO.size());
        //根据退租结算单 查询合同信息
        List<Long> contractId =
                settlementDO.stream().map(SettlementDO::getContractId).distinct().collect(Collectors.toList());
        List<CompanyContractDO> companyContractDOS = companyContractMapper.selectCompanyContractByIds(contractId);
        if (CollectionUtils.isEmpty(companyContractDOS)) {
            throw exception(CONTRACT_DOES_NOT_EXISTS);
        }
        List<BankTransferReqDTO> bankTransferReqDTOS = new ArrayList<>();
        settlementDO.forEach(item -> {
            BankTransferReqDTO bankTransferReqDTO = new BankTransferReqDTO();
            bankTransferReqDTO
                    .setOperator(SecurityFrameworkUtils.getLoginUser().getNickname());
            bankTransferReqDTO.setBizDate(LocalDate.now());
            bankTransferReqDTO.setBizNo(item.getSettlementNo());
            bankTransferReqDTO.setBizType(SourceBizTypeEnum.SETTLEMENT_BILL.name());
            bankTransferReqDTO.setApartmentId(item.getApartmentId());
            bankTransferReqDTO.setApartmentName(item.getApartmentName());
            companyContractDOS.stream().filter(fb -> fb.getId().compareTo(item.getContractId()) == 0).findFirst()
                    .ifPresent(p -> {
                        bankTransferReqDTO.setContractNo(p.getContractNo());
                        bankTransferReqDTO.setPayeeName(StringUtils.isNotBlank(item.getCardholder())?
                                item.getCardholder():StringUtils.isNotBlank(item.getCustomerName()) ?
                                item.getCustomerName() :
                                p.getCompanyName());
                    });
            bankTransferReqDTO.setPayeeMobile(item.getCustomerPhone());
            bankTransferReqDTO.setPayeeType("企业");
            bankTransferReqDTO.setPayAmount(item.getAmount());
            bankTransferReqDTO.setPayeeBank(item.getCustomerBank());
            bankTransferReqDTO.setPayeeBankBranch(item.getCustomerBankArea());
            bankTransferReqDTO.setPayeeBankCode(item.getBankCode());
            bankTransferReqDTO.setPayeeBankCity(item.getBankCity());
            bankTransferReqDTO.setPayeeBankProvince(item.getBankProvince());
            bankTransferReqDTO.setPayeeBankAccount(item.getCustomerCreditCard());
            bankTransferReqDTO.setReason(item.getEvictionReason());
            bankTransferReqDTO.setTenantId(item.getTenantId());
            bankTransferReqDTO.setDeptId(item.getDeptId());
            bankTransferReqDTOS.add(bankTransferReqDTO);
        });
        financeApi.bankTransferBatch(bankTransferReqDTOS);

    }

    @Override
    public void settlementConfirmList(List<Long> ids) {
        LambdaQueryWrapper<SettlementDO> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.in(SettlementDO::getId, ids);
        List<SettlementDO> settlementDO = settlementMapper.selectList(lambdaQueryWrapper);
        if (CollectionUtils.isEmpty(settlementDO)) {
            return;
        }
        //是否存在 除 无需审批 和 已生效的 的结算端
        List<SettlementDO> collect = settlementDO.stream()
                .filter(item -> item.getClearedStatus().equals(SettlementStatusEnum.IN_REVIEW.getCode())
                        ||item.getClearedStatus().equals(SettlementStatusEnum.REJECT.getCode())
                        ||item.getClearedStatus().equals(SettlementStatusEnum.CLEAR.getCode())
                ).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(collect)){
            throw exception(SETTLEMENT_SETTLED);
        }

        List<SettlementDO> flowList = settlementDO.stream().filter(item -> !item.getFlowType().equals("out") )
                .collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(flowList)){
            throw exception(SETTLEMENT_EXIST_UNCLEARABLE);
        }

        settlementDO.forEach(item->{
            item.setClearedStatus(SettlementStatusEnum.CLEAR.getCode());
        });
        //批量 修改结算单
        settlementMapper.updateBatch(settlementDO,settlementDO.size());
        //根据退租结算单 查询合同信息
        Collection<Long> contractId = settlementDO.stream().map(SettlementDO::getContractId).collect(Collectors.toList());
        List<ContractRoomDO> contractRoomDOS = contractRoomService.getList(contractId);
        if (CollectionUtils.isEmpty(contractRoomDOS)) {
            throw exception(CONTRACT_DOES_NOT_EXISTS);

        }
        Map<Long, ContractRoomDO> contractRoomDOMap = contractRoomDOS.stream().collect(Collectors.toMap(ContractRoomDO::getId, Function.identity()));

        List<BankTransferReqDTO> bankTransferReqDTOS=new ArrayList<>();
        settlementDO.forEach(item->{
            ContractRoomDO contractRoomDO = contractRoomDOMap.get(item.getContractId());
            if (contractRoomDO==null) {
                log.info("item:{}", JSON.toJSONString(item));
                throw exception(CONTRACT_DOES_NOT_EXISTS);
            }
            BankTransferReqDTO bankTransferReqDTO = new BankTransferReqDTO();
            bankTransferReqDTO
                    .setOperator(SecurityFrameworkUtils.getLoginUser().getNickname());
            bankTransferReqDTO.setBizDate(LocalDate.now());
            bankTransferReqDTO.setBizNo(item.getSettlementNo());
            bankTransferReqDTO.setBizType(SourceBizTypeEnum.SETTLEMENT_BILL.name());
            bankTransferReqDTO.setContractNo(contractRoomDO.getContractNo());
            bankTransferReqDTO.setApartmentId(contractRoomDO.getApartmentId());
            bankTransferReqDTO.setApartmentName(contractRoomDO.getApartmentName());
            bankTransferReqDTO.setPayeeName(StringUtils.isNotBlank(item.getCardholder()) ? item.getCardholder() :
                    StringUtils.isNotBlank(item.getCustomerName())?item.getCustomerName():contractRoomDO.getCustomerName());
            bankTransferReqDTO.setPayeeMobile(item.getCustomerPhone());
            bankTransferReqDTO.setPayeeType("租客");
            bankTransferReqDTO.setPayAmount(item.getAmount());
            bankTransferReqDTO.setPayeeBank(item.getCustomerBank());
            bankTransferReqDTO.setPayeeBankBranch(item.getCustomerBankArea());
            bankTransferReqDTO.setPayeeBankCode(item.getBankCode());
            bankTransferReqDTO.setPayeeBankCity(item.getBankCity());
            bankTransferReqDTO.setPayeeBankProvince(item.getBankProvince());
            bankTransferReqDTO.setPayeeBankAccount(item.getCustomerCreditCard());
            bankTransferReqDTO.setReason(item.getEvictionReason());
            bankTransferReqDTO.setTenantId(item.getTenantId());
            bankTransferReqDTO.setDeptId(item.getDeptId());
            bankTransferReqDTOS.add(bankTransferReqDTO);
        });
        financeApi.bankTransferBatch(bankTransferReqDTOS);
    }


    @Transactional
    @Override
    public void companySettlementConfirm(SettlementReqVO reqVO) {
        SettlementDO settlementDO = settlementMapper.selectById(reqVO.getId());
        if (settlementDO == null) {
            return;
        }
        //此次清算企业合同里的所有子合同更改清算状态
        List<Long> settlementIds = settlementMapper.selectSettlementIdByParentId(reqVO.getId());
        //修改子合同为已清算
        settlementMapper.updateClearedStatusByIds(settlementIds);
        //查看此合同下是否还有已生效,待入住的合同
        int num = contractRoomService.countNormalContractByCompanyContractId(settlementDO.getContractId());
        if (num == 0) {
            SettlementDO update = SettlementDO.builder()
                    .id(settlementDO.getId())
                    .clearedStatus(SettlementStatusEnum.CLEAR.getCode())
                    .build();
            if (StringUtils.isNotBlank(reqVO.getRemark())) {
                if (StringUtils.isNotBlank(settlementDO.getRemark())) {
                    update.setRemark(settlementDO.getRemark() + ";" + reqVO.getRemark());
                } else {
                    update.setRemark(reqVO.getRemark());
                }
            }
            //修改企业合同为清算状态
            settlementMapper.updateById(update);
        }

        ContractRoomDO contractRoomDO = contractRoomService.get(settlementDO.getContractId());
        settlementConfirm("企业", contractRoomDO, settlementDO, reqVO);
    }

    private void settlementConfirm(String payeeType, ContractRoomDO contractRoomDO, SettlementDO settlementDO, SettlementReqVO reqVO) {
        BankTransferReqDTO bankTransferReqDTO = new BankTransferReqDTO();
        bankTransferReqDTO
                .setOperator(SecurityFrameworkUtils.getLoginUser().getNickname());
        bankTransferReqDTO.setBizDate(LocalDate.now());
        bankTransferReqDTO.setBizNo(settlementDO.getSettlementNo());
        bankTransferReqDTO.setBizType(SourceBizTypeEnum.SETTLEMENT_BILL.name());
        bankTransferReqDTO.setContractNo(contractRoomDO.getContractNo());
        bankTransferReqDTO.setApartmentId(contractRoomDO.getApartmentId());
        bankTransferReqDTO.setApartmentName(contractRoomDO.getApartmentName());
        bankTransferReqDTO.setRoomId(contractRoomDO.getRoomId());
        bankTransferReqDTO.setRoomName(contractRoomDO.getRoomName());
        bankTransferReqDTO.setDepartment(settlementDO.getApartmentName());
        bankTransferReqDTO.setPayeeName(reqVO.getPayeeName());
        bankTransferReqDTO.setPayeeMobile(settlementDO.getCustomerPhone());
        bankTransferReqDTO.setPayeeType(payeeType);
        bankTransferReqDTO.setPayAmount(settlementDO.getAmount());
        bankTransferReqDTO.setPayeeBank(reqVO.getPayeeBank());
        bankTransferReqDTO.setPayeeBankBranch(reqVO.getPayeeBankBranch());
        bankTransferReqDTO.setPayeeBankCode(reqVO.getPayeeBankCode());
        bankTransferReqDTO.setPayeeBankCity(reqVO.getPayeeBankCity());
        bankTransferReqDTO.setPayeeBankProvince(reqVO.getPayeeBankProvince());
        bankTransferReqDTO.setPayeeBankAccount(reqVO.getPayeeBankAccount());
        bankTransferReqDTO.setReason(settlementDO.getEvictionReason());
        bankTransferReqDTO.setAttachment(reqVO.getAttachment());
        bankTransferReqDTO.setTenantId(settlementDO.getTenantId());
        bankTransferReqDTO.setDeptId(settlementDO.getDeptId());
        bankTransferReqDTO.setRemark(reqVO.getRemark());

        CommonResult<Void> result = financeApi.bankTransfer(bankTransferReqDTO);
        result.checkError();
    }

    @Override
    public List<SettlementExtDO> getSettlementPdfList(List<String> ids) {
        return settlementMapper.pageContractList(new ContractSettlementGetListReqVO(ids));
    }

    @Override
    public List<SettlementExtDO> getSettlementCompanyPdfList(List<String> ids) {
        return settlementMapper.pageContractBatchList(new ContractSettlementGetListReqVO(ids));
    }

    @Override
    public List<SettlementDetailDO> getSettlementDetail(Long id) {
        return settlementDetailMapper.selectList("settlement_id", id);
    }

    @Override
    public void updateStatus(Long id, String status) {
        SettlementDO settlementDO = new SettlementDO();
        settlementDO.setId(id);
        settlementDO.setClearedStatus(status);
        settlementMapper.updateById(settlementDO);
    }

    @Override
    public Long getSettlementCount() {
        LambdaQueryWrapper<SettlementDO> qw = new LambdaQueryWrapper<>();
        qw.in(SettlementDO::getClearedStatus, Arrays.asList(SettlementStatusEnum.NO_AUDIT_REQUITED.getCode(),
                SettlementStatusEnum.HAVE_TAKEN_EFFECT.getCode()));
        return settlementMapper.selectCount(qw);
    }

    @Override
    public RentOrderCountVO getSettlementDetailCount(Long id) {

        List<SettlementDetailDO> settlementDetailCount = settlementMapper.getSettlementDetailCount(id);
        RentOrderCountVO rentOrderCountVO = new RentOrderCountVO();
        if (CollectionUtils.isNotEmpty(settlementDetailCount)) {
            for (SettlementDetailDO item : settlementDetailCount) {
                BigDecimal negate = item.getRefundableAmount();
                if ("out".equals(item.getFlowType())) {
                    negate = item.getRefundableAmount().negate();
                }
                if (ApiConstants.RENT_FEE_ID.equals(item.getFeeId())) {
                    BigDecimal rentAmount = rentOrderCountVO.getRentAmount() == null ? BigDecimal.ZERO : rentOrderCountVO.getRentAmount();
                    rentOrderCountVO.setRentAmount(rentAmount.add(negate));
                }
                if (ApiConstants.DEPOSIT_FEE_ID.equals(item.getFeeId())) {
                    BigDecimal depositAmount = rentOrderCountVO.getDepositAmount() == null ? BigDecimal.ZERO : rentOrderCountVO.getDepositAmount();
                    rentOrderCountVO.setDepositAmount(depositAmount.add(negate));
                }
                if (!ApiConstants.DEPOSIT_FEE_ID.equals(item.getFeeId()) && !ApiConstants.RENT_FEE_ID.equals(item.getFeeId())) {
                    BigDecimal extrasAmount = rentOrderCountVO.getExtrasAmount() == null ? BigDecimal.ZERO : rentOrderCountVO.getExtrasAmount();
                    rentOrderCountVO.setExtrasAmount(extrasAmount.add(negate));
                }
                BigDecimal countAmount = rentOrderCountVO.getCountAmount() == null ? BigDecimal.ZERO : rentOrderCountVO.getCountAmount();
                rentOrderCountVO.setCountAmount(countAmount.add(negate));
            }
        }

        return rentOrderCountVO;
    }

    @Override
    public List<ContractSettlementExcelVO> exportSettlementExcel(ContractSettlementReqExcelVO req) {
        ContractSettlementGetListReqVO reqVO = SettlementConvert.INSTANCE.convert(req);
        List<SettlementExtDO> settlementExtDOS = settlementMapper.pageContractList(reqVO);

        if (CollectionUtils.isNotEmpty(settlementExtDOS)) {
            List<SettlementExtDO> collect = settlementExtDOS.stream().filter(f -> f.getClearedStatus() != null)
                    .peek(m -> {
                        m.setClearedStatus(transitionStatus(m.getClearedStatus()));
                        if ("out".equals(m.getFlowType())) {
                            m.setFlowType(OrderFeeFlowTypeEnum.OUTFLOW.getName());
                        } else {
                            m.setFlowType(OrderFeeFlowTypeEnum.INFLOW.getName());
                        }
                    }).collect(Collectors.toList());

            List<ContractSettlementExcelVO> list = SettlementConvert.INSTANCE.convert01(collect);
            Optional.ofNullable(list).ifPresent(m -> IntStream.range(0, m.size()).forEach(i -> {
                m.get(i).setSerialNumber(i + 1);
            }));
            return list;
        }

        return Collections.emptyList();
    }

    @Override
    public List<ContractSettlementExcelVO> exportSettlemenCompanyBatchtExcel(ContractSettlementReqExcelVO req) {
        ContractSettlementGetListReqVO reqVO = SettlementConvert.INSTANCE.convert(req);
        List<SettlementExtDO> settlementExtDOS = settlementMapper.pageContractBatchList(reqVO);

        if (CollectionUtils.isNotEmpty(settlementExtDOS)) {
            List<SettlementExtDO> collect = settlementExtDOS.stream().filter(f -> f.getClearedStatus() != null)
                    .peek(m -> {
                        m.setClearedStatus(transitionStatus(m.getClearedStatus()));
                        if ("out".equals(m.getFlowType())) {
                            m.setFlowType(OrderFeeFlowTypeEnum.OUTFLOW.getName());
                        } else {
                            m.setFlowType(OrderFeeFlowTypeEnum.INFLOW.getName());
                        }
                    }).collect(Collectors.toList());

            List<ContractSettlementExcelVO> list = SettlementConvert.INSTANCE.convert01(collect);
            Optional.ofNullable(list).ifPresent(m -> IntStream.range(0, m.size()).forEach(i -> {
                m.get(i).setSerialNumber(i + 1);
            }));
            return list;
        }

        return Collections.emptyList();
    }

    @Override
    public ContractSettlementListReqVO getSettlementByProcessInstanceId(String id) {
        //查询总清单
        SettlementDO settlementDO = settlementMapper.selectOne(SettlementDO::getProcessInstanceId, id);
        if (settlementDO == null) {
            return null;
        }
        ContractSettlementListReqVO reqVO = ContractSettlementConvert.INSTANCE.convert(settlementDO);
        //查询合同周期
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(settlementDO.getContractId());
        if (contractRoomDO != null) {
            reqVO.setStartTime(contractRoomDO.getStartTime());
            reqVO.setEndTime(contractRoomDO.getEndTime());
        }
        //查询清单明细
        List<SettlementDetailDO> settlementDetailDOS = settlementDetailMapper.selectList(SettlementDetailDO::getSettlementId, settlementDO.getId());

        return processingData(settlementDetailDOS, reqVO);
    }

    @Override
    public BatchSettlementOrderFeeVO batchSettlement(List<Long> ids, LocalDate endTime, String settlementType) {
        rentOrderService.unpaidOrder(endTime, ids, Arrays.asList(OrderTypeEnum.DEFAULT.getCode(),
                OrderTypeEnum.CUSTOM.getCode()));
        List<ContractRoomDO> contracts = contractRoomService.getList(ids);
        if (CollectionUtils.isEmpty(contracts) || contracts.size() != ids.size()) {
            throw new ServiceException(ErrorCodeConstants.CONTRACT_NOT_EXISTS.getCode(), "退租合同数量与查询数据不一致，请刷新后重试！！！");
        }
        // 合同所有账单
        List<RentOrderDO> rentOrdersb = rentOrderMapper.selectList(new LambdaQueryWrapper<RentOrderDO>()
                .in(RentOrderDO::getBusinessId, ids)
                .in(RentOrderDO::getOrderType, Arrays.asList(OrderTypeEnum.DEFAULT.getCode(),
                        OrderTypeEnum.CUSTOM.getCode())));
        if (CollectionUtil.isEmpty(rentOrdersb)) {
            throw new ServiceException(ErrorCodeConstants.CONTRACT_NOT_EXISTS.getCode(), "未找到账单！！！");
        }
        // 合同所有费用
        List<OrderFeesDO> feesAll = orderFeesMapper.selectList(OrderFeesDO::getOrderId,
                rentOrdersb.stream().map(RentOrderDO::getId).collect(Collectors.toList()));
        if (CollectionUtil.isEmpty(feesAll)) {
            throw new ServiceException(ErrorCodeConstants.CONTRACT_NOT_EXISTS.getCode(), "未找到账单明细！！！");
        }
        //分组
        Map<Long, List<RentOrderDO>> collect =
                rentOrdersb.stream().collect(Collectors.groupingBy(RentOrderDO::getBusinessId));
        List<SettlementOrderFeeVO> clearingOrder = new ArrayList<>();
        collect.forEach((k, rentOrders) -> {
            contracts.stream().filter(co -> co.getId().compareTo(k) == 0).findFirst().ifPresent(contract -> {
                List<OrderFeesDO> fees = new ArrayList<>();
                //单个合同账单明细
                rentOrders.stream().forEach(ro -> {
                    Long id = ro.getId();
                    List<OrderFeesDO> collect1 =
                            feesAll.stream().filter(fl -> fl.getOrderId().compareTo(id) == 0).collect(Collectors.toList());
                    if(CollectionUtil.isNotEmpty(collect1)){
                        fees.addAll(collect1);
                    }
                });

                // 已支付需要结算的账单
                List<RentOrderDO> settlementOrders = rentOrders.stream()
                        // 自动生成和手动添加的账单
                        .filter(o -> OrderTypeEnum.DEFAULT.getCode().equals(o.getOrderType())
                                || OrderTypeEnum.CUSTOM.getCode().equals(o.getOrderType()))
                        // 当期及以后的账单
                        .filter(o -> o.getStartTime().compareTo(endTime) <= 0 && o.getEndTime().compareTo(endTime) >= 0
                                || o.getStartTime().isAfter(endTime))
                        // 已经支付或部分支付
                        .filter(o -> OrderPayStatusEnum.PAID.getCode().equals(o.getPayStatus())
                                || OrderPayStatusEnum.PART.getCode().equals(o.getPayStatus()))
                        .collect(Collectors.toList());
                Map<Long, RentOrderDO> rentOrderIds = settlementOrders.stream().collect(Collectors.toMap(RentOrderDO::getId, Function.identity()));

                // 押金费用
                List<OrderFeesDO> deposit = fees.stream()
                        .filter(f -> OrderFeeSortTypeEnum.DEPOSIT.getCode().equals(f.getFeeSort()))
                        .filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                        // 只要是提前退, 就不退押金
                        .filter(f -> ContractNodeEnum.EXCHANGE.getCode().equals(settlementType) || contract.getEndTime().compareTo(endTime) <= 0)
                        .collect(Collectors.toList());

                // 其它已支付周期费用(只结转周期性费用)
                List<OrderFeesDO> others = fees.stream()
                        .filter(f -> OrderFeeSortTypeEnum.CYCLE.getCode().equals(f.getFeeSort()))
                        .filter(f -> rentOrderIds.containsKey(f.getOrderId()))
                        .sorted(Comparator.comparing(OrderFeesDO::getOrderId))
                        .collect(Collectors.toList());
                // 计算实际产生费用
                List<SettlementOrderFeeVO> list = calculateActualFeeByDailyFee(contract, others, endTime, rentOrderIds);
                // 押金及待结转费用
                list.addAll(deposit.stream().map(d -> convert(d, endTime)
                        .setRefundableAmount(d.getAmount())).collect(Collectors.toList()));
                //写入合同id
                if(CollectionUtil.isNotEmpty(list)){
                    list.forEach(lf->lf.setContractId(k));
                    clearingOrder.addAll(list);
                }

            });

        });
        BatchSettlementOrderFeeVO vo  = new BatchSettlementOrderFeeVO();
        vo.setClearingOrder(clearingOrder);
        //汇总
        if(CollectionUtil.isNotEmpty(clearingOrder)){
            Map<BatchFeeVO, List<SettlementOrderFeeVO>> collect1 = clearingOrder.stream()
                    .collect(Collectors.groupingBy(gm -> BatchFeeVO.builder().feeId(gm.getFeeId()).feeName(gm.getFeeName()).build()));
            List<SettlementOrderFeeVO> collect2 = collect1.values().stream().map(v -> {
                SettlementOrderFeeVO feeVO = new SettlementOrderFeeVO();
                feeVO.setFeeName(v.get(0).getFeeName()).setFeeId(v.get(0).getFeeId());
                feeVO.setOrderStartDate(v.stream().min(Comparator.comparing(SettlementOrderFeeVO::getOrderStartDate)).get().getOrderStartDate())
                        .setOrderEndDate(v.stream().max(Comparator.comparing(SettlementOrderFeeVO::getOrderStartDate)).get().getOrderEndDate())
                        .setAmount(v.stream().map(SettlementOrderFeeVO::getAmount).reduce(BigDecimal.ZERO,
                                (p, q) -> p.add(q)))
                        .setRefundableAmount(v.stream().map(SettlementOrderFeeVO::getRefundableAmount).reduce(BigDecimal.ZERO, (p, q) -> p.add(q)));
                return feeVO;
            }).collect(Collectors.toList());
            vo.setSummaryClearingForm(collect2);
        }

        return vo;
    }

    /**
     * 获取清算子项
     *
     * @param id
     * @return
     */
    @Override
    public List<SettlementDO> getSettlementSubitemByParentId(Long id) {
        return settlementMapper.selectList(SettlementDO::getParentSettlementId, id);
    }
}
